package com.gitee.loyo.controller;

import com.gitee.loyo.BridgeConfig;
import com.gitee.loyo.Global;
import com.gitee.loyo.Jumpers;
import com.gitee.loyo.Pages;
import com.gitee.loyo.page.JumpPage;
import com.gitee.loyo.utils.HttpUtils;
import com.gitee.loyo.utils.NetUtils;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServerRequest;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.*;
import java.io.StringWriter;

public class JumpController {
    private final Logger log = LoggerFactory.getLogger(JumpController.class);

    @GET
    @Path("/jump")
    public void getEndpoints(HttpServerRequest request, Promise<Void> promise){
        JumpPage jumpPage = new JumpPage();
        jumpPage.jumpers = BridgeConfig.INSTANCE.jumpers;

        Template tmpl = Global.VELOCITY_ENGINE.getTemplate("vm/jump.vm");
        VelocityContext ctx = new VelocityContext();
        ctx.put("page", jumpPage);
        StringWriter output = new StringWriter();
        tmpl.merge(ctx, output);
        Pages.renderPage(request.response(), output.toString());
        promise.complete();
    }

    @POST
    @Path("/jump")
    public void saveJumper(HttpServerRequest request, Promise<Void> promise){
        Future.<Jumpers.Jumper>future(p ->
            HttpUtils.getBodyAsJson(request, Jumpers.Jumper.class, p)
        ).compose(jumper -> {
            if(jumper.port <= 0 || jumper.remoteHost == null || jumper.remoteHost.isEmpty() || jumper.remotePort <= 0){
                return Future.failedFuture("illegal data");
            }
            return Jumpers.add(jumper);
        }).onComplete(ar -> completion(ar, request, promise));
    }

    @POST
    @Path("/jump/startup")
    public void startupJumper(HttpServerRequest request, Promise<Void> promise){
        int port = Integer.parseInt(request.getParam("port"));
        Jumpers.startup(Vertx.currentContext().owner(), port).onComplete(ar -> completion(ar, request, promise));
    }

    @POST
    @Path("/jump/shutdown")
    public void shutdownJumper(HttpServerRequest request, Promise<Void> promise){
        int port = Integer.parseInt(request.getParam("port"));
        Jumpers.shutdown(port).onComplete(ar -> completion(ar, request, promise));
    }

    @PUT
    @Path("/jump")
    public void updateJumper(HttpServerRequest request, Promise<Void> promise){
        Future.<Jumpers.Jumper>future(p ->
            HttpUtils.getBodyAsJson(request, Jumpers.Jumper.class, p)
        ).compose(jumper -> {
            if(jumper.port <= 0 || jumper.remoteHost == null || jumper.remoteHost.isEmpty() || jumper.remotePort <= 0){
                return Future.failedFuture("illegal data");
            }
            return Jumpers.update(jumper);
        }).onComplete(ar -> completion(ar, request, promise));
    }

    @DELETE
    @Path("/jump")
    public void deleteJumper(HttpServerRequest request, Promise<Void> promise){
        Future.<Jumpers.Jumper>future(p ->
            HttpUtils.getBodyAsJson(request, Jumpers.Jumper.class, p)
        ).compose(jumper -> {
            if(jumper.port <= 0){
                return Future.failedFuture("illegal data");
            }
            return Jumpers.remove(jumper.port);
        }).onComplete(ar -> completion(ar, request, promise));
    }

    @HEAD
    @Path("/jump")
    public void pingJumper(HttpServerRequest request, Promise<Void> promise){
        Future.<Void>future(p -> {
            String port = request.getParam("port");
            if(port == null || port.isEmpty()){
                p.fail("illegal data");
                return;
            }
            Jumpers.Jumper jumper = Jumpers.get(Integer.parseInt(port));
            NetUtils.ping(jumper.remoteHost, jumper.remotePort).onComplete(p);
        }).onComplete(ar -> {
            if(ar.failed()){
                log.error("ping error", ar.cause());
                request.response().setStatusCode(400).end();
            }else{
                request.response().setStatusCode(204).end();
            }
            promise.complete();
        });
    }

    private void completion(AsyncResult<Void> ar, HttpServerRequest request, Promise<Void> promise){
        if(ar.failed()){
            ar.cause().printStackTrace();
            request.response().setStatusCode(400).end(ar.cause().getMessage());
        }else{
            request.response().setStatusCode(200).end();
        }
        promise.complete();
    }
}
